﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace MarshalClasses
{
    public static class DBReWrapper<TSource, TDest>
            where TSource : class, new()
            where TDest : class, new()
    {
        static DBReWrapper()
        {
            if (Attribute.GetCustomAttribute(typeof (TDest), typeof (DBSourceClassAttribute)) == null)
                throw new InvalidOperationException(string.Format("{0} cannot be binded to {1}", typeof(TDest).FullName, typeof(TSource).FullName));

            var flags = BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public;
            var sourceProps = typeof(TSource).GetProperties(flags);
            var destProps = new List<PropertyInfo>(typeof(TDest).GetProperties(flags));

            foreach (var p in sourceProps)
            {
                var dp = destProps.Find(pi => pi.Name == p.Name);
                if (dp != null)
                {
                    __PropsToCopy.Add(new KeyValuePair<PropertyInfo, PropertyInfo>(p, dp));
                }
            }

            if (__PropsToCopy.Count == 0)
            {
                throw new InvalidOperationException("There is no matched properties to copy");
            }
        }

        public static TDest Rewrap(TSource source)
        {
            var r = new TDest();
            Copy(source, r);
            return r;
        }

        public static List<TDest> Rewrap(IList<TSource> source)
        {
            var res = new List<TDest>(source.Count);
            var count = source.Count;
            for (int i = 0; i < count; ++i)
            {
                res.Add(Rewrap(source[i]));
            }
            return res;
        }

        public static List<TDest> Rewrap(IEnumerable<TSource> source)
        {
            return Rewrap(new List<TSource>(source));
        }

        private static void Copy(TSource source, TDest dest)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (dest == null)
            {
                throw new ArgumentNullException("dest");
            }

            foreach (var p in __PropsToCopy)
            {
                var v = p.Key.GetValue(source, null);
                p.Value.SetValue(dest, v, null);
            }
        }

        private static readonly List<KeyValuePair<PropertyInfo, PropertyInfo>> __PropsToCopy = new List<KeyValuePair<PropertyInfo, PropertyInfo>>();
    }

    [AttributeUsage(AttributeTargets.Class)]
    public class DBSourceClassAttribute : Attribute
    {
        public DBSourceClassAttribute(Type dbClass)
        {
            DBClass = dbClass;
        }

        public readonly Type DBClass;
    }

    [DBSourceClass(typeof(DB.User))]
    public class User
    {
        public int ID { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string Email { get; set; }
    }

    [DBSourceClass(typeof(DB.Tag))]
    public class Tag
    {
        public int ID { get; set; }

        public int? ParentID { get; set; }

        public string Name { get; set; }
    }

    [DBSourceClass(typeof(DB.Album))]
    public class Album
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public int OwnerUserRef { get; set; }
    }

    [DBSourceClass(typeof(DB.MObject))]
    public class MObject
    {
        public int ID { get; set; }

        public int ParentAlbumRef { get; set; }

        public byte Type { get; set; }
    }
}
